#include "GameField.h"

#include <QtGui/QPaintEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QPainter>
#include <QtCore/QTimer>
#include <QtCore/QTime>

GameField::GameField(QWidget *parent)
   :QWidget(parent), pixmap(800,600), shiftPoint(0,0), len(3), startlen(3),
    pause(false),level(1), live(3), finishLen(15) {

    setFocusPolicy(Qt::StrongFocus);
    qsrand(QTime::currentTime().second());

    napr = Right;
    tempState = Right;

    timer = new QTimer(this);
    timer->setInterval(150);
    connect(timer, SIGNAL(timeout()), this, SLOT(rebuild()));

    createTimer = new QTimer(this);
    createTimer->setInterval(4000);
    connect(createTimer, SIGNAL(timeout()), SLOT(createFoot()));

    coordSoliter();
        createLevel();
    drawScen();
}

GameField::~GameField() {
}


/*
 * Creating the level map
 */
void GameField::createLevel()
{
    // Scene's borders
    for(int i=0; i!=44; i++)
        rectScen << QPoint(0, i);
    for(int i=0; i!=65; i++)
        rectScen << QPoint(i, 44);
    for(int i=0; i!=44; i++)
        rectScen << QPoint(64, i);
    for(int i=0; i!=64; i++)
        rectScen << QPoint(i, 0);

    //
        switch(level) {
    case 1:
                currentLevel.clear();
        break;
    case 2:
        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(32, i);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(32, i);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(32, i);
        for(int i=28; i!=33; i++)
                        currentLevel << QPoint(32, i);
        for(int i=36; i!=41; i++)
                        currentLevel << QPoint(32, i);
        break;
    case 3:
        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(32, i);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(32, i);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(32, i);
        for(int i=28; i!=33; i++)
                        currentLevel << QPoint(32, i);
        for(int i=36; i!=41; i++)
                        currentLevel << QPoint(32, i);

        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=28; i!=37; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=40; i!=45; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=48; i!=53; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=56; i!=61; i++)
                        currentLevel << QPoint(i, 22);
        break;
    case 4:
        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(32, i);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(32, i);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(32, i);
        for(int i=28; i!=33; i++)
                        currentLevel << QPoint(32, i);
        for(int i=36; i!=41; i++)
                        currentLevel << QPoint(32, i);

        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=28; i!=37; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=40; i!=45; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=48; i!=53; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=56; i!=61; i++)
                        currentLevel << QPoint(i, 22);

        for(int i=11; i!=29; i++)
                        currentLevel << QPoint(i, 10);
        for(int i=10; i!=19; i++)
                        currentLevel << QPoint(10, i);
        for(int i=26; i!=35; i++)
                        currentLevel << QPoint(10, i);
        for(int i=11; i!=29; i++)
                        currentLevel << QPoint(i, 34);

        for(int i=36; i!=54; i++)
                        currentLevel << QPoint(i, 10);
        for(int i=10; i!=19; i++)
                        currentLevel << QPoint(54, i);
        for(int i=26; i!=35; i++)
                        currentLevel << QPoint(54, i);
        for(int i=36; i!=54; i++)
                        currentLevel << QPoint(i, 34);
        break;
    case 5:
        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(32, i);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(32, i);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(32, i);
        for(int i=28; i!=33; i++)
                        currentLevel << QPoint(32, i);
        for(int i=36; i!=41; i++)
                        currentLevel << QPoint(32, i);

        for(int i=4; i!=9; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=12; i!=17; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=20; i!=25; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=28; i!=37; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=40; i!=45; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=48; i!=53; i++)
                        currentLevel << QPoint(i, 22);
        for(int i=56; i!=61; i++)
                        currentLevel << QPoint(i, 22);

        for(int i=11; i!=29; i++)
                        currentLevel << QPoint(i, 10);
        for(int i=10; i!=19; i++)
                        currentLevel << QPoint(10, i);
        for(int i=26; i!=35; i++)
                        currentLevel << QPoint(10, i);
        for(int i=11; i!=29; i++)
                        currentLevel << QPoint(i, 34);

        for(int i=36; i!=54; i++)
                        currentLevel << QPoint(i, 10);
        for(int i=10; i!=19; i++)
                        currentLevel << QPoint(54, i);
        for(int i=26; i!=35; i++)
                        currentLevel << QPoint(54, i);
        for(int i=36; i!=54; i++)
                        currentLevel << QPoint(i, 34);

        for(int i=14; i!=32; i++)
                        currentLevel << QPoint(i, 14);
        for(int i=15; i!=22; i++)
                        currentLevel << QPoint(14, i);

        for(int i=33; i!=51; i++)
                        currentLevel << QPoint(i, 30);
        for(int i=23; i!=30; i++)
                        currentLevel << QPoint(50, i);
        break;
    default:
                currentLevel.clear();
    }
}

/*
 * Redefinition of paint event
 */
void GameField::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(0,0,pixmap); // drawing of the level
    drawFoot(&painter);             // drawing of the food
    drawSoliter(&painter);          // drawing of the snake

    // if no lifes left, game over message
    if(!live) {
        painter.setPen(Qt::white);
        painter.setFont(QFont("Segoe Print", 38));
        painter.drawText(rect() ,Qt::AlignCenter, tr("Game Over"));
    }

}

/*
 * Redefinition of keypress event
 * Giving the direction of snake's movement
 * Restricting the reverse movement
 * Space = pause
 *
 */
void GameField::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_Up:
        if (napr != Down) {
            if(!timer->isActive() && live && !pause) timer->start();
            tempState = Up;
            shiftPoint = QPoint(0,-1);
        }
        break;
    case Qt::Key_Down:
        if (napr != Up) {
            if(!timer->isActive() && live && !pause) timer->start();
            tempState = Down;
            shiftPoint = QPoint(0,1);
        }
        break;
    case Qt::Key_Left:
        if (napr != Right) {
            if(!timer->isActive() && live && !pause) timer->start();
            tempState = Left;
            shiftPoint = QPoint(-1,0);
        }
        break;
    case Qt::Key_Right:
        if (napr != Left) {
            if(!timer->isActive() && live && !pause) timer->start();
            tempState = Right;
            shiftPoint = QPoint(1,0);
        }
        break;
    case Qt::Key_Space:
        if(pause && live) {
            pause = false;
            timer->start();
        } else {
            pause = true;
            timer->stop();
            createTimer->stop();
        }
        break;
    default:
        QWidget::keyPressEvent(event); // passing the event further
    }
}

/*
 * Creation of the level.
 * In order not to redraw it every time, create it on QPixmap
 *
 */
void GameField::drawScen()
{
    QPainter painter(&pixmap);
    painter.setPen(Qt::darkGray);
    painter.setBrush(QBrush(Qt::darkGray));

        createLevel();          // creating the level map
    pixmap.fill(Qt::black);     // cleaning the scene - filling with black

    // drawing of the scene borders
    for(int i=0; i!=rectScen.size(); i++)
        drawElemScen(&painter, rectScen.point(i).x(), rectScen.point(i).y());

    // отрисовываем на сцене карту уровня
        for(int i=0; i!=currentLevel.size(); i++)
                drawElemScen(&painter, currentLevel.point(i).x(), currentLevel.point(i).y());

        switch(level) {
    case 1:
        finishLen = 15;
        break;
    case 2:
        finishLen = 26;
        break;
    case 3:
        finishLen = 30;
        break;
    case 4:
        finishLen = 35;
        break;
    case 5:
        finishLen = 40;
        break;
    default:
        finishLen =9999999;
    }
    emit setTarget(finishLen);
}

/*
 * Creation of the scene's element (square)
 * (X,Y) - position of the square in the scene's coordinates
 * The size of the segment is 12x12
 * The size of the scene is 65x45
 * The size of the each element is 10x10
 *
 */
void GameField::drawElemScen(QPainter *painter, int X, int Y)
{
    QRectF rect(6,6,10,10);
    rect.moveCenter(rect.center() + QPointF(12*X, 12*Y));
    painter->drawRect(rect);
}

void GameField::drawSoliter(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::red);
    painter->setBrush(QBrush(Qt::red));

    drawElemScen(painter, soliter.first().x(), soliter.first().y());
    painter->setPen(Qt::darkGray);
    painter->setBrush(QBrush(Qt::darkGray));

    for(int i=1; i!=soliter.size(); i++) {
        drawElemScen(painter, soliter.point(i).x(), soliter.point(i).y());
    }

    painter->restore();
}

void GameField::drawFoot(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::red);
    painter->setBrush(QBrush(Qt::red));

    for(int i=0; i!=foot.size(); i++) {
        drawElemScen(painter, foot.point(i).x(), foot.point(i).y());
    }

    painter->restore();
}

void GameField::rebuild()
{
    napr = tempState;
    if(!createTimer->isActive()) createTimer->start();

    // Moving the snake in the direction of movement, except for the head
    for(int i=soliter.size()-1; i!=0; i--) {
        soliter.setPoint(i,soliter.point(i-1));
    }

    // Calculating the next position of the head
    QPoint point = soliter.first() + shiftPoint;
    // If there is food at the point, increase the snake and remove that food from the list
    if(foot.contains(point)) {
        soliter.prepend(point);
        foot.remove(foot.indexOf(point));
        emit lenSoliter(soliter.size());
                if(soliter.size() == finishLen) nextLevel();
        } else if(soliter.contains(point) || currentLevel.contains(point)) {
        dead();
    } else if (point.x()== 0 || point.x() == 64 || point.y() == 0 || point.y() == 44) {
        dead();
    } else {
        soliter.first() = point;
    }

    update();
}

void GameField::createFoot()
{
    if(foot.size() < 3) {
        forever {
            QPoint point((qrand()%63)+1, (qrand()%43)+1);
                        if (!soliter.contains(point) && !currentLevel.contains(point)) {
                foot << point;
                break;
            }
        }
    }

}

void GameField::newGame()
{
    napr = Right;
    pause = false;
    timer->stop();
    createTimer->stop();
    soliter.clear();
    foot.clear();
        level = 1;
    live = 3;
    coordSoliter();
    emit setLive(live);
        emit setLevel(level);
    emit lenSoliter(3);
    drawScen();
    update();
}

void GameField::dead()
{
    napr = Right;
    pause = true;
    timer->stop();
    createTimer->stop();
    live -= 1;
    emit setLive(live);
    emit lenSoliter(3);
    QTimer::singleShot(1500, this, SLOT(pauseUpdate()));
}

void GameField::pauseUpdate()
{
    if(live) {
        pause = false;
        soliter.clear();
        foot.clear();
        coordSoliter();
    }
    update();
}

void GameField::nextLevel()
{
        level++;
    live++;
        emit setLevel(level);
    emit setLive(live);
    emit lenSoliter(3);

    napr = Right;
    timer->stop();
    createTimer->stop();

    drawScen();
    coordSoliter();

    foot.clear();
    update();

}


void GameField::coordSoliter()
{
    soliter.clear();
        switch(level) {
        case 1:
            soliter << QPoint(33,22) << QPoint(32,22) << QPoint(31,22);
            break;
        case 2:
            soliter << QPoint(6,22) << QPoint(5,22) << QPoint(4,22);
            break;
        case 3:
            soliter << QPoint(6,8) << QPoint(5,8) << QPoint(4,8);
            break;
        case 4:
            soliter << QPoint(6,3) << QPoint(5,3) << QPoint(4,3);
            break;
        default:
            soliter << QPoint(6,3) << QPoint(5,3) << QPoint(4,3);
    }
}


